package com.study.injectsample;

import android.app.Activity;
import android.view.View;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 作者： zjf 10/6/20 4:38 PM
 * 参考：
 * 描述：
 */
public class AnnotationUtils {
    public static void bind(Activity activity) {
        /**
         * 获取绑定的对象的class对象
         */
        Class<? extends Activity> clazz = activity.getClass();

        /**
         * 获取类的成员变量(包括私有)
         */
        Field[] fields = clazz.getDeclaredFields();

        /**
         * 获取类的方法
         */
        Method[] methods = clazz.getDeclaredMethods();

        /**
         * 遍历成员变量
         */
        for (Field field : fields) {
            //如果被BindView注解走此方法
            if (field.isAnnotationPresent(BindView.class)) {
                //如果被注解,拿到注解的实例
                BindView bindView = field.getAnnotation(BindView.class);
                //拿到注解的value的值
                int id = bindView.value();
                //设置允许访问权限
                field.setAccessible(true);
                //根据id拿到控件的实例
                View view = activity.findViewById(id);
                try {
                    //给控件变量赋值
                    field.set(activity, view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
            //如果被IntentParam注解走此方法
            else if (field.isAnnotationPresent(IntentParam.class)) {
                IntentParam intentParam = field.getAnnotation(IntentParam.class);
                String value = intentParam.value();
                String paramValue = activity.getIntent().getStringExtra(value);

                field.setAccessible(true);
                try {
                    field.set(activity, paramValue);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }

        /**
         * 遍历方法
         */
        for (Method method : methods) {
            Annotation[] annotations = method.getAnnotations();
            /**
             * 遍历方法上的注解
             */
            for (Annotation annotation : annotations) {
                /**
                 * 获取注解的类
                 */
                Class<? extends Annotation> annotationType = annotation.annotationType();

                //isAnnotationPresent()如果指定类型的注解存在于此元素上返回true
                if (annotationType.isAnnotationPresent(EventType.class)) {
                    //获取到注解上的元注解
                    EventType eventType = annotationType.getAnnotation(EventType.class);
                    //获取点击事件的类
                    Class<?> listenerType = eventType.listenerType();
                    //获取事件的方法名
                    String listenerSetter = eventType.listenerSetter();

                    try {
                        //由于在绑定点击事件的注解上,绑定了元注解,所以我们在这里
                        // 不必关心用户使用哪种方式
                        // 注意:获取注解类的value方法,因为这里的value是一个int数组,所以不能直接使用.value()获取
                        Method valueMethod = annotationType.getDeclaredMethod("value");
                        //由于value()方法是annotation注解里面的方法,所以把annotation传递给invoke()去执行
                        int[] viewIds = (int[]) valueMethod.invoke(annotation);
                        //打开被注解的方法的访问权限
                        method.setAccessible(true);

                        ListenerInvocationHandler<Activity> handler = new ListenerInvocationHandler<>(method, activity);
                        /**
                         * 关键点:获取到View.OnClickListener接口的代理
                         */
                        Object listenerProxy = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class<?>[]{listenerType}, handler);
                        //遍历注解value里面的值
                        for (int id : viewIds) {
                            /**
                             * 找到View
                             */
                            View view = activity.findViewById(id);

                            /**
                             *获得View的点击事件
                             * 方法getMethod():
                             * 参数1:方法名(setOnLongClickListener或setOnClickListener)
                             * 参数2:方法参数的class对象(View.OnLongClickListener或View.OnClickListener)
                             */
                            Method setListener = view.getClass().getMethod(listenerSetter, listenerType);

                            /**
                             * 这里可以理解为执行了View的setOnClickListener()方法,方法参数是我们创建的View.OnClickListener的代理对象
                             */
                            setListener.invoke(view, listenerProxy);
                        }

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 代理的回调
     */
    private static class ListenerInvocationHandler<T> implements InvocationHandler {
        private Method method;
        private T target;

        public ListenerInvocationHandler(Method method, T target) {
            this.method = method;
            this.target = target;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            /**
             * 因为这里代理了View.OnClickListener接口,所以每次执行View的setOnClickListener(),View.OnClickListener接口的onClick()方法会回调到这里 查看126行
             * 我们把onClick()替换为被我们注解的方法
             */
            return this.method.invoke(target, args);
        }
    }
}
